/* This file is part of MyRename
 * Copyright (C) 2005-2009 Kai-Chieh Ku (kjackie@gmail.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

// ControlPanel.cpp : implementation file
//

#include "stdafx.h"
#include "MyRename.h"
#include "MyRenameDlg.h"
#include "preferences.h"
#include "pcre/PCREPP/PCREPP.hpp"
#include "others.h"
#include "language.h"
#include "undofile.h"
#include "../UAOLib/UAOLib.h"
#include "ControlPanel.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define UPLIST() {FILEWND->UpdateList();}

// CControlPanel dialog

IMPLEMENT_DYNAMIC(CControlPanel, CDialog)
CControlPanel::CControlPanel(CWnd* pParent /*=NULL*/)
	: CDialog(CControlPanel::IDD, pParent), m_bIsRenaming(false), m_wndParent(pParent)
{
}

CControlPanel::~CControlPanel()
{
}

void CControlPanel::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);

	//{{AFX_DATA_MAP(CControlPanel)
	DDX_Control(pDX, IDC_FN_INPUT, m_xecFilename);
	DDX_Control(pDX, IDC_EXT_INPUT, m_xecExtension);
	DDX_Control(pDX, IDC_NUM_START_INPUT, m_xecNumStart);
	DDX_Control(pDX, IDC_NUM_DIFF_INPUT, m_xecNumDiff);
	DDX_Control(pDX, IDC_NUM_WIDTH_INPUT, m_xecNumWidth);
	DDX_Control(pDX, IDC_RP_FIND, m_xecFind);
	DDX_Control(pDX, IDC_RP_REPLACE, m_xecReplace);
	DDX_Control(pDX, IDC_MASK_INPUT, m_xecMask);
	DDX_Text(pDX, IDC_FN_INPUT, thePrefs.sFnInput);
	DDX_Text(pDX, IDC_EXT_INPUT, thePrefs.sExtInput);
	DDX_Text(pDX, IDC_NUM_START_INPUT, thePrefs.iNumStart);
	DDV_MinMaxInt(pDX, thePrefs.iNumStart, -9999999, 99999999, 1);
	DDX_Text(pDX, IDC_NUM_DIFF_INPUT, thePrefs.iNumDiff);
	DDV_MinMaxInt(pDX, thePrefs.iNumDiff, -9999999, 99999999, 1);
	DDX_Text(pDX, IDC_NUM_WIDTH_INPUT, thePrefs.iNumWidth);
	DDV_MinMaxInt(pDX, thePrefs.iNumWidth, 1, 256, 2);
	DDX_Text(pDX, IDC_RP_FIND, thePrefs.sRpFind);
	DDX_Text(pDX, IDC_RP_REPLACE, thePrefs.sRpReplace);
	DDX_Text(pDX, IDC_MASK_INPUT, thePrefs.sFtMask);
	DDX_Check(pDX, IDC_NUM_ENABLE, thePrefs.bNumEnable);
	DDX_Check(pDX, IDC_RP_ENABLE, thePrefs.bRpEnable);
	DDX_Check(pDX, IDC_RP_RX, thePrefs.bRxEnable);
	DDX_Check(pDX, IDC_RP_MATCHCASE, thePrefs.bRpMatchCase);
	DDX_Check(pDX, IDC_OTHER_BIG5FIX, thePrefs.bBig5Fix);
	DDX_Check(pDX, IDC_OTHER_FULL2HALF, thePrefs.bFullToHalf);
	//}}AFX_DATA_MAP
}

void CControlPanel::DDV_MinMaxInt(CDataExchange* pDX, int &value, int minVal, int maxVal, int def/* = INT_MIN*/)
{
	ASSERT(maxVal > minVal);

	def = max(def, minVal); 

	if (value < minVal || value > maxVal) {
		if (pDX->m_bSaveAndValidate) {//control ---> var
			CString str_msg, str_title(GetResString(IDS_APP));
			EDITBALLOONTIP tip = {0};
			str_msg.Format(GetResString(IDS_ERR_MINMAX) , minVal, maxVal);
			tip.cbStruct = sizeof(EDITBALLOONTIP);
			tip.pszTitle = str_title;
			tip.pszText = str_msg;
			tip.ttiIcon = TTI_ERROR;
			GetDlgItem(pDX->m_idLastControl)->SendMessage(EM_SHOWBALLOONTIP, 0, (LPARAM) &tip);
		}
		value = def;
	}
}

BEGIN_MESSAGE_MAP(CControlPanel, CDialog)
	//{{AFX_MSG_MAP(CControlPanel)
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_BUTTON_APPLY, &CControlPanel::OnBnClickedButtonApply)
END_MESSAGE_MAP()

BOOL CControlPanel::OnInitDialog()
{
	CDialog::OnInitDialog();

	m_xecFilename.ModifyXECStyle(XEC_RIGHTBTN);
	m_xecExtension.ModifyXECStyle(XEC_RIGHTBTN);

	m_xecFilename.LimitText(_MAX_FNAME);
	m_xecExtension.LimitText(_MAX_EXT);

	m_xecNumStart.LimitText(8);
	m_xecNumDiff.LimitText(8);
	m_xecNumWidth.LimitText(3);

	m_xecNumStart.SetDefaultText(_T("1"));
	m_xecNumDiff.SetDefaultText(_T("1"));
	m_xecNumWidth.SetDefaultText(_T("2"));

	m_xecNumStart.ModifyXECStyle(XEC_INTEGER | XEC_MINMAX);
	m_xecNumDiff.ModifyXECStyle(XEC_INTEGER | XEC_MINMAX);
	m_xecNumWidth.ModifyXECStyle(XEC_INTEGER | XEC_MINMAX);

	m_xecNumStart.SetMinMax(-9999999, 99999999);
	m_xecNumDiff.SetMinMax(-9999999, 99999999);
	m_xecNumWidth.SetMinMax(1, 256);

	SetFilenameMode(thePrefs.iFnMode);
	m_xecFilename.EnableWindow(GetFilenameMode() != IDC_FN_NONE);

	SetExtensionMode(thePrefs.iExtMode);
	m_xecExtension.EnableWindow(GetExtensionMode() == IDC_EXT_REPLACE);

	SetCaseMode(thePrefs.iCaseMode);

	m_xecFind.ModifyXECStyle(XEC_RIGHTBTN);
	m_xecReplace.ModifyXECStyle(XEC_RIGHTBTN);

	m_xecFind.LimitText(MAX_PATH);
	m_xecReplace.LimitText(MAX_PATH);

	m_xecFind.EnableWindow(thePrefs.bRpEnable);
	m_xecReplace.EnableWindow(thePrefs.bRpEnable);

	m_xecMask.LimitText(MAX_PATH);

	return TRUE;
}

void CControlPanel::Localize()
{
	SetDlgItemText(IDC_GRP_FN, GetResString(IDS_GRP_FN));
	SetDlgItemText(IDC_FN_NONE, GetResString(IDS_NONE));
	SetDlgItemText(IDC_FN_REPLACE, GetResString(IDS_REPLACE));
	SetDlgItemText(IDC_FN_BEFORE, GetResString(IDS_FN_BEFORE));
	SetDlgItemText(IDC_FN_AFTER, GetResString(IDS_FN_AFTER));
	SetDlgItemText(IDC_GRP_EXT, GetResString(IDS_GRP_EXT));
	SetDlgItemText(IDC_EXT_NONE, GetResString(IDS_NONE));
	SetDlgItemText(IDC_EXT_UPPER, GetResString(IDS_EXT_UPPER));
	SetDlgItemText(IDC_EXT_LOWER, GetResString(IDS_EXT_LOWER));
	SetDlgItemText(IDC_EXT_REPLACE, GetResString(IDS_REPLACE));
	SetDlgItemText(IDC_GRP_CASE, GetResString(IDS_GRP_CASE));
	SetDlgItemText(IDC_CASE_NONE, GetResString(IDS_NONE));
	SetDlgItemText(IDC_CASE_UPPER, GetResString(IDS_CASE_UPPER));
	SetDlgItemText(IDC_CASE_LOWER, GetResString(IDS_CASE_LOWER));
	SetDlgItemText(IDC_CASE_UPF, GetResString(IDS_CASE_UPF));
	SetDlgItemText(IDC_GRP_NUMBER, GetResString(IDS_GRP_NUMBER));
	SetDlgItemText(IDC_NUM_ENABLE, GetResString(IDS_ENABLE));
	SetDlgItemText(IDC_NUM_START, GetResString(IDS_NUM_START));
	SetDlgItemText(IDC_NUM_DIFF, GetResString(IDS_NUM_DIFF));
	SetDlgItemText(IDC_NUM_WIDTH, GetResString(IDS_NUM_WIDTH));
	SetDlgItemText(IDC_GRP_REPLACE, GetResString(IDS_GRP_REPLACE));
	SetDlgItemText(IDC_RP_ENABLE, GetResString(IDS_ENABLE));
	SetDlgItemText(IDC_RP_RX, GetResString(IDS_RP_RX));
	SetDlgItemText(IDC_RP_MATCHCASE, GetResString(IDS_MATCH_CASE));
	SetDlgItemText(IDC_TX_FIND, GetResString(IDS_RP_FIND));
	SetDlgItemText(IDC_TX_REPLACE, GetResString(IDS_REPLACE));
	SetDlgItemText(IDC_MASK, GetResString(IDS_MASK));
	SetDlgItemText(IDC_BTN_RENAME, GetResString(IDS_RENAME));
	SetDlgItemText(IDC_GRP_OTHER, GetResString(IDS_GRP_OTHER));
	SetDlgItemText(IDC_OTHER_BIG5FIX, GetResString(IDS_BIG5FIX));
	SetDlgItemText(IDC_OTHER_FULL2HALF, GetResString(IDS_FULL2HALF));

	CXEditCtrl::Localize();
}

int CControlPanel::GetFilenameMode()
{
	return GetCheckedRadioButton(IDC_FN_NONE, IDC_FN_AFTER);
}

void CControlPanel::SetFilenameMode(int nIDCheckButton)
{
	ASSERT(nIDCheckButton <= IDC_FN_AFTER && nIDCheckButton >= IDC_FN_NONE);
	CheckRadioButton(IDC_FN_NONE, IDC_FN_AFTER, nIDCheckButton);
}

int CControlPanel::GetExtensionMode()
{
	return GetCheckedRadioButton(IDC_EXT_NONE, IDC_EXT_REPLACE);
}

void CControlPanel::SetExtensionMode(int nIDCheckButton)
{
	ASSERT(nIDCheckButton <= IDC_EXT_REPLACE && nIDCheckButton >= IDC_EXT_NONE);
	CheckRadioButton(IDC_EXT_NONE, IDC_EXT_REPLACE, nIDCheckButton);
}

int CControlPanel::GetCaseMode()
{
	return GetCheckedRadioButton(IDC_CASE_NONE, IDC_CASE_UPF);
}

void CControlPanel::SetCaseMode(int nIDCheckButton)
{
	ASSERT(nIDCheckButton <= IDC_CASE_UPF && nIDCheckButton >= IDC_CASE_NONE);
	CheckRadioButton(IDC_CASE_NONE, IDC_CASE_UPF, nIDCheckButton);
}

#define RPALLVAR(s)	{s.Replace(_T(":name"), varName);\
					s.Replace(_T(":title"), varTitle);\
					s.Replace(_T(":ext"), varExt);\
					s.Replace(_T(":num"), varNum);}

CString& CControlPanel::strchange(CString &dest, const CString &src, const bool isDir)
{
	return CTRPANWND->_strchange(dest, src, isDir);
}

CString& CControlPanel::_strchange(CString &dest, const CString &src, const bool isDir)
{
	CString tmp, ext, fn, find, replace;
	CString varName, varTitle, varExt, varNum;
	int dot = src.ReverseFind(_T('.'));

	varName = src;
	varTitle = (dot == -1 || isDir) ? src : src.Left(dot);
	if (dot != -1 && !isDir) varExt = src.Mid(dot + 1);
	varNum.Format(_T("%0*d"), m_iWidth, m_iStart);

	dest = src;

#ifdef _UNICODE
	if (thePrefs.bBig5Fix) {
		Big5UnicodeToUnicode(dest.GetBuffer());
		dest.ReleaseBuffer();
	}
	if (thePrefs.bFullToHalf) {
		FullToHalf(dest.GetBuffer());
		dest.ReleaseBuffer();
	}
#endif

	//process filename --->
	tmp = thePrefs.sFnInput;
	RPALLVAR(tmp)
	switch (thePrefs.iFnMode) {
		case IDC_FN_REPLACE:
			if (dot != -1 && !isDir)
				dest = tmp + dest.Mid(dot);
			else
				dest = tmp;
			break;
		case IDC_FN_BEFORE:
			dest = tmp + dest;
			break;
		case IDC_FN_AFTER:
			if (dot != -1 && !isDir)
				dest.Insert(dot, tmp);
			else
				dest += tmp;
			break;
	}
	//process filename <---

	//process extension --->
	if (!isDir) {
		dot = dest.ReverseFind(_T('.'));
		if (dot != -1) {
			tmp = thePrefs.sExtInput;
			RPALLVAR(tmp)
			ext = dest.Mid(dot);
			fn = dest.Left(dot);
			switch (thePrefs.iExtMode) {
				case IDC_EXT_UPPER:
					dest = fn + ext.MakeUpper();
					break;
				case IDC_EXT_LOWER:
					dest = fn + ext.MakeLower();
					break;
				case IDC_EXT_REPLACE:
					dest = fn + _T(".") + tmp;
					break;
			}
		}
	}
	//process extension <---

	//process case --->
	dot = dest.ReverseFind(_T('.'));
	if (dot != -1 && !isDir) {
		fn = dest.Left(dot);
		ext = dest.Mid(dot);
	}
	else {
		fn = dest;
		ext = _T("");
	}
	switch (thePrefs.iCaseMode) {
		case IDC_CASE_UPPER:
			dest = fn.MakeUpper() + ext;
			break;
		case IDC_CASE_LOWER:
			dest = fn.MakeLower() + ext;
			break;
		case IDC_CASE_UPF:
			tmp = fn.Left(1);
			fn = fn.Mid(1);
			dest = tmp.MakeUpper() + fn.MakeLower() + ext;
			break;
	}
	//process case <---

	//process number --->
	if (thePrefs.bNumEnable) {
		tmp = varNum;
		dot = dest.ReverseFind(_T('.'));
		if (dot != -1 && !isDir) {
			fn = dest.Left(dot);
			ext = dest.Mid(dot);
		}
		else {
			fn = dest;
			ext = _T("");
		}
		switch (thePrefs.iFnMode) {
			case IDC_FN_BEFORE:
				dest = tmp + dest;
				break;
			default:
				dest = fn + tmp + ext;
				break;
		}
	}
	m_iStart += m_iDiff;
	//process number <---

	//process replace --->
	find = thePrefs.sRpFind;
	if (thePrefs.bRpEnable && !find.IsEmpty()) {
		replace = thePrefs.sRpReplace;
		RPALLVAR(find)
		RPALLVAR(replace)
		if (thePrefs.bRxEnable) {
#ifdef _UNICODE
			VERIFY(PCRE::replace(find, replace, dest, (thePrefs.bRpMatchCase ? 0 : PCRE_CASELESS) | PCRE_UTF8) == 0);
#else
			VERIFY(PCRE::replace(find, replace, dest, (thePrefs.bRpMatchCase ? 0 : PCRE_CASELESS)) == 0);
#endif
		}
		else {
			if (thePrefs.bRpMatchCase) dest.Replace(find, replace);
			else ReplaceNoCase(dest, find, replace);
		}
	}
	//process replace <---

	return dest;
}

#undef RPALLVAR

void CControlPanel::ConvStart()
{
	UpdateData();
	thePrefs.fnMask.setFileName(thePrefs.sFtMask, thePrefs.fnMask.m_attribute);
	m_iStart = thePrefs.iNumStart;
	m_iDiff = thePrefs.iNumDiff;
	m_iWidth = thePrefs.iNumWidth;
}

void CControlPanel::DoRename()
{
	if (m_bIsRenaming)
		return;

	AutoUnlock RenameLocker(m_bIsRenaming);
	AutoUnlock ReturnLocker(theApp.m_bSkipReturn);

	if (thePrefs.bConfirmRn)
		if (MessageBox(GetResString(IDS_RENAME_CONFIRM), NULL, MB_OKCANCEL | MB_ICONQUESTION) != IDOK)
			return;

	CWaitCursor wait;

	{
		AutoUnlock UpdataLocker(FILEWND->m_bLockUpdate);
		
		CFileName **aBefore, **aAfter;
		CString tmpDir, tmpPath;
		BOOL bConfirmUd;

		try {
			CFileListCtrl *before = &(FILEWND->m_PreList), *after = &(FILEWND->m_PostList);
			bool sel = false;
			int count = before->GetItemCount(), i, t;

			if (count == 0)
				throw 2;

			if (before->GetSelectedCount() > 0) {
				count = before->GetSelectedCount();
				sel = true;
			}

			aBefore = new CFileName * [count];
			aAfter = new CFileName * [count];
			//save the filenames that should be renamed later --->
			if (sel) {
				POSITION pos = before->GetFirstSelectedItemPosition();
				ASSERT(pos != NULL);
				for (i = 0;pos && i < count;i++) {
					t = before->GetNextSelectedItem(pos);
					aBefore[i] = (CFileName *) before->GetItemData(t);
					aAfter[i] = (CFileName *) after->GetItemData(t);
				}
			}
			else {
				for (i = 0;i < count;i++) {
					aBefore[i] = (CFileName *) before->GetItemData(i);
					aAfter[i] = (CFileName *) after->GetItemData(i);
				}
			}
			//save the filenames that should be renamed later <---

			//create undo files (push) --->
			CreateDirectory(_T("undo"), NULL);
			TCHAR u1[20], u2[20];
			DeleteFile(_T("undo\\undo10.undo"));
			for (i = 10;i >= 2;i--) {
				_stprintf(u1, _T("undo\\undo%02d.undo"), i - 1);
				_stprintf(u2, _T("undo\\undo%02d.undo"), i);
				MoveFile(u1, u2);
			}
			//create undo files (push) <---
				
			CString bFn, aFn;
			int respon;

			//undo --- save undo information during renaming (used when renaming operation is failed)
			//tmpUndo --- actual undo file (will rename to undo01.undo when renaming is successful)
			CUndoFile undo(_T("undo\\undo01.undo"), UNDO_WRITE), tmpUndo(_T("undo\\tmp"), UNDO_WRITE);

			//initilize undo files
			if (! undo.InitWrite(thePrefs.sLastPath) || !tmpUndo.InitWrite(thePrefs.sLastPath))
				if (MessageBox(GetResString(IDS_RENAME_UNDOFAIL), NULL, MB_OKCANCEL | MB_ICONERROR) == IDOK)
					throw 1;

			//get temporary directive name
			if (tmpdir(thePrefs.sLastPath, tmpDir.GetBufferSetLength(_MAX_FNAME)) == NULL)
				throw 1;

			tmpDir.ReleaseBuffer();
			tmpPath.Format(_T("%s%s\\"), thePrefs.sLastPath, tmpDir);
			bConfirmUd = thePrefs.bConfirmUd; //save this setting
			thePrefs.bConfirmUd = FALSE;

			//renaming (1: rename it and move it to temporary directive) --->
			for (i = 0;i < count;i++) {
				bFn = thePrefs.sLastPath + aBefore[i]->GetName();
				aFn = tmpPath + aAfter[i]->GetName();
				if (!MoveFile(bFn, aFn)) {
					respon = MessageBox(LastErrorMsg(), NULL, MB_CANCELTRYCONTINUE | MB_ICONERROR);
					if (respon != IDTRYAGAIN && respon != IDCONTINUE) {
						undo.WriteFooter();
						undo.close();
						DoUndo();
						throw 1;
					}
				}
				else if (! undo.WriteSession(aBefore[i]->GetName(), tmpDir + _T('\\') + aAfter[i]->GetName()) ||
					! tmpUndo.WriteSession(aBefore[i]->GetName(), aAfter[i]->GetName()))
					MessageBox(GetResString(IDS_RENAME_UNDOFAIL), NULL, MB_OK | MB_ICONERROR);
			}
			//renaming (1: rename it and move it to temporary directive) <---

			BOOL bFound;
			CFileFind finder;

			bFound = finder.FindFile(tmpPath + _T("*.*"));
			//renaming (2: move it back) --->
			while (bFound) {
				bFound = finder.FindNextFile();
				if (!finder.IsDots()) {
					bFn = tmpPath + finder.GetFileName();
					aFn = thePrefs.sLastPath + finder.GetFileName();
					if (!MoveFile(bFn, aFn)) {
						respon = MessageBox(LastErrorMsg(), NULL, MB_CANCELTRYCONTINUE | MB_ICONERROR);
						if (respon != IDTRYAGAIN && respon != IDCONTINUE) {
							undo.WriteFooter();
							undo.close();
							DoUndo();
							throw 1;
						}					
					}
					else if (! undo.WriteSession(tmpDir + _T('\\') + finder.GetFileName(), finder.GetFileName()))
						MessageBox(GetResString(IDS_RENAME_UNDOFAIL), NULL, MB_OK | MB_ICONERROR);
				}
			}
			//renaming (2: move it back) <---
			finder.Close();

			thePrefs.bConfirmUd = bConfirmUd; //restore its setting

			undo.close();
			tmpUndo.WriteFooter();
			tmpUndo.close();
			DeleteFile(_T("undo\\undo01.undo"));
			MoveFile(_T("undo\\tmp"), _T("undo\\undo01.undo"));
		} catch (const int ex) {
			switch (ex) {
				case 1:
					thePrefs.bConfirmUd = bConfirmUd; //restore its setting
					break;
				case 2:
					return;
				default:
					delete [] aBefore;
					delete [] aAfter;
					throw ex;
					break;
			}
		}

		RemoveDirectory(tmpPath);
		delete [] aBefore;
		delete [] aAfter;
	}

	FILEWND->UpdateList(true, &thePrefs.fnMask);
}

uint8 CControlPanel::UndoAva()
{
	CreateDirectory(_T("undo"), NULL);
	uint8 i;
	CFileFind find;
	BOOL bFound;

	bFound = find.FindFile(_T("undo\\undo*.undo"));

	for (i = 0;bFound;i++)
		bFound = find.FindNextFile();

	return i;
}

#define UNDOASSERT(exp) {if (!(exp)) throw 1;}

void CControlPanel::DoUndo()
{
	if (! UndoAva())
		return;

	bool ok = true;
	CUndoFile undo;
	CString workpath, tmpDir;

	try {
		UNDOASSERT(undo.open(_T("undo\\undo01.undo"), UNDO_READ));
		UNDOASSERT(undo.InitRead());
		CString msg;
		workpath = undo.GetWorkPath();
		msg.Format(GetResString(IDS_UNDO_CONFIRM), workpath);
		if (thePrefs.bConfirmUd)
			if (MessageBox(msg, NULL, MB_OKCANCEL | MB_ICONQUESTION) != IDOK)
				return;

		CString sBefore, sAfter;
		FILEWND->LockUpdate();

		if (tmpdir(workpath, tmpDir.GetBufferSetLength(_MAX_FNAME)) == NULL) {
			ok = false;
			throw 3;
		}
		tmpDir.ReleaseBuffer();

		SetCurrentDirectory(workpath);
		//renaming (1: rename it and move it to temporary directive) --->
		while (undo.ReadSession(sBefore, sAfter) == READ_OK) {
			if (!MoveFile(sAfter, tmpDir + _T('\\') + sBefore))
				ok = false;
		}
		//renaming (1: rename it and move it to temporary directive) <---
		BOOL bFound;
		CFileFind finder;

		bFound = finder.FindFile(tmpDir + _T("\\*.*"));
		
		//renaming (2: move it back) --->
		while (bFound) {
			bFound = finder.FindNextFile();
			if (!finder.IsDots()) {
				if (!MoveFile(tmpDir + _T('\\') + finder.GetFileName(), finder.GetFileName()))
					ok = false;
			}
		}
		//renaming (2: move it back) <---
		finder.Close();

		RemoveDirectory(tmpDir);
		SetCurrentDirectory(thePrefs.sAppDir);
		throw 2;
	} catch (const int ex) {
		if (ex < 1 || ex > 3) {
			FILEWND->UnlockUpdate();
			throw ex;
		}

		undo.close();
		FILEWND->UnlockUpdate();

		//delete a undo file (pop) --->
		if (ex == 1 || ex == 2) {
			TCHAR u1[20], u2[20];
			DeleteFile(_T("undo\\undo01.undo"));
			int i;
			for (i = 1;i <= 9;i++) {
				_stprintf(u1, _T("undo\\undo%02d.undo"), i + 1);
				_stprintf(u2, _T("undo\\undo%02d.undo"), i);
				MoveFile(u1, u2);
			}
		}
		//delete a undo file (pop) <---

		if (ex == 1) {
			MessageBox(GetResString(IDS_UNDO_BROKEN), NULL, MB_OK | MB_ICONERROR);
			return;
		}
	}

	if (ok)
		MessageBox(GetResString(IDS_UNDO_OK), NULL, MB_OK | MB_ICONINFORMATION);
	else
		MessageBox(GetResString(IDS_UNDO_FAIL), NULL, MB_OK | MB_ICONWARNING);

	if (workpath == thePrefs.sLastPath)
		FILEWND->UpdateList(true, &thePrefs.fnMask);
}

#undef UNDOASSERT

// CControlPanel message handlers

void CControlPanel::OnBnClickedFn()
{
	if (thePrefs.iFnMode != GetFilenameMode()) {
		thePrefs.iFnMode = GetFilenameMode();
		m_xecFilename.EnableWindow(GetFilenameMode() != IDC_FN_NONE);
		FILEWND->UpdateList();
	}
}

void CControlPanel::OnBnClickedExt()
{
	if (thePrefs.iExtMode != GetExtensionMode()) {
		thePrefs.iExtMode = GetExtensionMode();
		m_xecExtension.EnableWindow(GetExtensionMode() == IDC_EXT_REPLACE);
		FILEWND->UpdateList();
	}
}

void CControlPanel::OnBnClickedCase()
{
	if (thePrefs.iCaseMode != GetCaseMode()) {
		thePrefs.iCaseMode = GetCaseMode();
		FILEWND->UpdateList();
	}
}

BOOL CControlPanel::OnCommand(WPARAM wParam, LPARAM lParam)
{
	switch (HIWORD(wParam)) {
		case EN_UPDATE:
			switch (LOWORD(wParam)) {
				case IDC_NUM_START_INPUT:
				case IDC_NUM_DIFF_INPUT:
				case IDC_NUM_WIDTH_INPUT:
				case IDC_FN_INPUT:
				case IDC_EXT_INPUT:
				case IDC_RP_FIND:
				case IDC_RP_REPLACE:
					goto UPDATE_LIST;
				case IDC_MASK_INPUT:
					FILEWND->UpdateList(true, &thePrefs.fnMask);
					return TRUE;
			}
			break;
		case BN_CLICKED:
			switch (LOWORD(wParam)) {
				case IDC_FN_NONE:
				case IDC_FN_REPLACE:
				case IDC_FN_AFTER:
				case IDC_FN_BEFORE:
					OnBnClickedFn();
					return TRUE;
				case IDC_CASE_NONE:
				case IDC_CASE_UPF:
				case IDC_CASE_UPPER:
				case IDC_CASE_LOWER:
					OnBnClickedCase();
					return TRUE;
				case IDC_EXT_NONE:
				case IDC_EXT_REPLACE:
				case IDC_EXT_UPPER:
				case IDC_EXT_LOWER:
					OnBnClickedExt();
					return TRUE;
				case IDC_FN_INPUT:
				case IDC_EXT_INPUT:
				case IDC_RP_FIND:
				case IDC_RP_REPLACE:
					//show variables menu
					GetDlgItem((int) (LOWORD(wParam)))->SendMessage(WM_COMMAND, wParam, lParam);
					return TRUE;
				case IDC_BTN_RENAME:
					DoRename();
					thePrefs.SaveSettings();
					return TRUE;
				case IDC_RP_ENABLE:
					UpdateData();
					m_xecFind.EnableWindow(thePrefs.bRpEnable);
					m_xecReplace.EnableWindow(thePrefs.bRpEnable);
					goto UPDATE_LIST;
				case IDC_RP_MATCHCASE:
				case IDC_RP_RX:
					UpdateData();
					if (thePrefs.bRpEnable)
						goto UPDATE_LIST;
					return TRUE;
				case IDC_NUM_ENABLE:
				case IDC_OTHER_BIG5FIX:
				case IDC_OTHER_FULL2HALF:
					goto UPDATE_LIST;
			}
			break;
	}

	return CDialog::OnCommand(wParam, lParam);

UPDATE_LIST:
	FILEWND->UpdateList();
	return TRUE;
}


void CControlPanel::OnBnClickedButtonApply()
{
	// TODO: Add your control notification handler code here
	CString strSelectedPath("");
	GetDlgItem(IDC_EDIT_PATH)->GetWindowText(strSelectedPath);
	((CMyRenameDlg*)m_wndParent)->m_pFilewnd->m_DirTree.SetSelectPath(strSelectedPath);
}
